using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace SlSyncLibrary
{
    /// <summary>
    /// Main class used to resync the APM model of Silverlight.
    /// </summary>
    public class Executor
    {
        static Queue<ExecutorOperation> executionQueue=new Queue<ExecutorOperation>();

        static Thread worker;
        static ManualResetEvent doLoop;
        static AutoResetEvent stoploop;
        public static bool isInBeginEndBlock=false;
        public static event EventHandler<BeginBlockEventArgs> BeginExecutionBlock;
        public static event EventHandler EndExecutionBlock;
        public static event EventHandler<FailedEventArgs> FailedExecutionBlock;
        public static event EventHandler<PromptEventArgs> OnPrompt;
        static PromptResult result;
        delegate void AskForPromptDelegate(string title, string message, bool inputBox, PromptButton button, PromptIcon icon);

        /// <summary>
        /// Stop the execution of the operations queue
        /// </summary>
        public static void Stop()
        {
            stoploop.Set();
        }

        /// <summary>
        /// Ctor, Initialize ResetEvents and WorkerThread
        /// </summary>
        static Executor()
        {
            stoploop=new AutoResetEvent(false);
            doLoop=new ManualResetEvent(false);
            worker=new Thread(new ThreadStart(ExecutionLoop));
            worker.Start();
        }

        /// <summary>
        /// Main logic, dequeue operation when notified from a EndBlock statement and process operations sync
        /// </summary>
        private static void ExecutionLoop()
        {
            WaitHandle[] events=new WaitHandle[] { stoploop, doLoop };

            while (true)
            {
                int eventId=WaitHandle.WaitAny(events);
                if (eventId==0) // stopped
                    break;
                else
                {
                    lock (executionQueue)
                    {
                        while (executionQueue.Count>0)
                        {
                            ExecutorOperation operation=executionQueue.Dequeue();
                            try
                            {
                                Debug.WriteLine("Executor Invoke: {0}", operation.Name);
                                operation.Invoke();
                            }
                            catch (Exception ex)
                            {
                                if (isInBeginEndBlock)
                                {
                                    // dequeue until end is reached
                                    while (true)
                                    {
                                        ExecutorOperation nullOperation=executionQueue.Dequeue();
                                        if (nullOperation.IsEndBlock)
                                        {
                                            MainDispatcher.Current.BeginInvoke(new Action<Exception, ExecutorOperation>(NotifyFailed), ex, operation);
                                            break;
                                        }
                                    }
                                }
                                else
                                    MainDispatcher.Current.BeginInvoke(new Action<Exception, ExecutorOperation>(NotifyFailed), ex, operation);
                            }
                        }
                        doLoop.Reset();
                    }
                }
            }
        }





        public static PromptResult CurrentPrompt { get { return result; } }

        //internal static PromptResult AskForPrompt(string title, string message, bool inputBox, PromptButton button, PromptIcon icon)
        //{
        //    if (OnPrompt == null)
        //        throw new NotImplementedException("You must attach Executor OnPrompt event");

        //    Executor.Invoke(new AskForPromptDelegate(_AskForPrompt), title, message, inputBox, button, icon);
        //    return result;
        //}



        internal static PromptResult _AskForPrompt(string title, string message, bool inputBox, PromptButton button, PromptIcon icon)
        {
            result=new PromptResult();
            MainDispatcher.Current.BeginInvoke(OnPrompt, null, new PromptEventArgs(title, message, inputBox, button, icon, result));
            result.WaitResult();
            return result;
        }

        private static void NotifyFailed(Exception e, ExecutorOperation operation)
        {
            if (FailedExecutionBlock!=null)
                FailedExecutionBlock(null, new FailedEventArgs(e, operation));
        }

        /// <summary>
        /// The first delegate that the executor needs in order to process the queue
        /// </summary>
        public static void BeginBlock()
        {
            BeginBlock(null);
        }

        public static void BeginBlock(string message)
        {
            if (isInBeginEndBlock)
                return;

            isInBeginEndBlock=true;
            Invoke(new Action<string>(NotifyBeginBlock), message);
        }

        /// <summary>
        ///  Notify the listeners that a BeginBlock have been signaled
        /// </summary>
        private static void NotifyBeginBlock(string msg)
        {
            if (BeginExecutionBlock!=null)
                MainDispatcher.Current.BeginInvoke(BeginExecutionBlock, null, new BeginBlockEventArgs(msg));

        }

        /// <summary>
        /// End Block is the starter delegate, once setted this delegate the Executor starts with the dequeuing of operations
        /// </summary>
        public static void EndBlock()
        {
            if (!isInBeginEndBlock)
                return;

            isInBeginEndBlock=false;
            Invoke(NotifyEndBlock);
            doLoop.Set(); // start dequeue block
        }

        /// <summary>
        /// Notidy the queue that an EndBlockStatement have been signaled into executor
        /// </summary>
        private static void NotifyEndBlock()
        {
            if (EndExecutionBlock!=null)
                MainDispatcher.Current.BeginInvoke(EndExecutionBlock, null, null);
        }


        public static ExecutorOperation Invoke(Action a)
        {
            lock (executionQueue)
            {
                ExecutorOperation operation=new ExecutorOperation(a);
                executionQueue.Enqueue(operation);
                if (!isInBeginEndBlock)
                    doLoop.Set();
                return operation;
            }
        }


        public static ExecutorOperation Invoke(Delegate d, params object[] args)
        {
            lock (executionQueue)
            {
                ExecutorOperation operation=new ExecutorOperation(d, args);
                executionQueue.Enqueue(operation);
                if (!isInBeginEndBlock)
                    doLoop.Set();
                return operation;
            }
        }

    }
}